#include "AutoScaleCullCallback.h"
#include <osgUtil/CullVisitor>
#include <osg/PositionAttitudeTransform>

AutoScaleCullCallback::AutoScaleCullCallback(const osg::Vec3d& baseScale /*= osg::Vec3d(1,1,1)*/, double minScale /*= 0.0*/, double maxScale /*= DBL_MAX*/):
_baseScale( baseScale ),
_minScale( minScale ),
_maxScale( maxScale )
{

}

void AutoScaleCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
{
	osg::PositionAttitudeTransform* pat = dynamic_cast<osg::PositionAttitudeTransform*>(node);
	if (pat==NULL)
	{
		return;
	}
	osgUtil::CullVisitor* cs = static_cast<osgUtil::CullVisitor*>(nv);
	osg::Camera* cam = cs->getCurrentCamera();

	// If this is an RTT camera, we need to use it's "parent"
	// to calculate the proper scale factor.
	if (cam->isRenderToTextureCamera() &&
		cam->getView() &&
		cam->getView()->getCamera() &&
		cam->getView()->getCamera() != cam)
	{
		cam = cam->getView()->getCamera();
	}

	if (cam->getViewport())
	{
		// Reset the scale so we get a proper bound
		pat->setScale(_baseScale);
		const osg::BoundingSphere& bs = node->getBound();

		// transform centroid to VIEW space:
		osg::Vec3d centerView = bs.center() * cam->getViewMatrix();

		// Set X coordinate to the radius so we can use the resulting CLIP
		// distance to calculate meters per pixel:
		centerView.x() = bs.radius();
		double fovy, ratio,zNear,zFar;
		cam->getProjectionMatrixAsPerspective(fovy,ratio,zNear,zFar);
		//centerView.z()=-zNear;

		// transform the CLIP space:
		osg::Vec3d centerClip = centerView * cam->getProjectionMatrix();

		// caluclate meters per pixel:
		double mpp = (centerClip.x()*0.5) * cam->getViewport()->width();

		// and the resulting scale we need to auto-scale.
		double scale = bs.radius() / mpp;

		if (scale < _minScale)
			scale = _minScale;
		else if (scale>_maxScale)
			scale = _maxScale;

		pat->setScale(osg::componentMultiply(_baseScale, osg::Vec3d(scale, scale, scale)));
	}

	if (node->getCullingActive() == false)
	{
		node->setCullingActive(true);
	}

	traverse(node, nv);
}
